using System;
using System.Collections;
using Microsoft.SPOT;

namespace Soigeneris
{
    /// <summary>
    /// A simple enum to keep track of TestPoints.
    /// Since Analog pins are less than zero it should be easy to test for
    /// a passed test point being Analog or Digital.
    /// </summary>
    public enum TestPoint
    {
        None = 0, A1 = -1, A2 = -2, ACNT = -3,
        D1 = 1, D2 = 2, D3 = 3, D4 = 4, D5 = 5, D6 = 6, D7 = 6, D8 = 8, DCNT = 9
    }

    //public struct TestPoint
    //{
    //    public enum Analog { A0, A1, ACNT = 2 };
    //    public enum Digital { D0, D1, D2, D3, D4, D5, D6, D7, DCNT = 8 };
    //}

    // A TestPort is just a way to abstract the I/O pins used to test a cable.
    // This is a base class that just includes the interface framework.
    // Sub classes should iron out the spcific details of talking to the 
    // physical I/O used (for instance one subclass could make use of
    //  mux chips to increase pin count.)
    //
    // We expose a certain number of digital and analog pins that can be used
    // for testing and the caller specifies which of those are actually being
    // used with the 'PinsUsed' structure.
    public class TestPort
    {
        public enum TestType { Digital, Analog };

        #region Helper_structs

        // encapsulate things related to keeping track of which pins, out of the
        // available pins on this test port are actually being used for this test
        public struct PinsUsed
        {
            Int32 _DigitalUsed; // bitmask of digital pins used
            Int32 _AnalogUsed;  // bitmask of analog pins used
            int _numDigitalPins;// count of digital pins used
            int _numAnalogPins; // count of analog pins used
            int _digitalIterator; // 
            int _analogIterator;  //

            /// <summary>
            /// Tell system which digital pins to use
            /// </summary>
            /// <param name="pin"></param>
            public void addDigitalPin(TestPoint pin)
            {
                _DigitalUsed |= (int)System.Math.Pow(2, (int)pin);
                _numDigitalPins++;
            }

            /// <summary>
            ///Ttell system which analog pins to use
            /// </summary>
            /// <param name="pin"></param>
            public void addAnalogPin(TestPoint pin)
            {
                _AnalogUsed |= (int)System.Math.Pow(2, (int)pin);
                _numAnalogPins++;
            }

            /// <summary>
            /// Reset all settings
            /// </summary>
            public void reset()
            {
                _DigitalUsed = _AnalogUsed = 0;
                _numDigitalPins = _numAnalogPins = 0;
                _digitalIterator = _analogIterator = 0;
            }

            /// <summary>
            /// return used digital pin bitmask
            /// </summary>
            public Int32 DigitalUsed
            {
                get
                {
                    return _DigitalUsed;
                }
            }

            /// <summary>
            /// Next used TestPoint
            /// Sort of a redneck itererator :)
            /// </summary>
            /// <returns>Next used TestPoint, 0 == end of list reached </returns>
            public int nextDPin()
            {
                do
                {
                    _digitalIterator++;

                } while (((TestPoint)_digitalIterator < TestPoint.DCNT)
                    && !((_DigitalUsed & (1 << _digitalIterator)) > 0));

                // stay within bounds, returning zero signal end of list
                if ((TestPoint)_digitalIterator >= TestPoint.DCNT 
                    | (TestPoint)_digitalIterator < TestPoint.D1)
                {
                    _digitalIterator = 0;
                }

                return _digitalIterator;
            }

            /// <summary>
            /// returns the next used analog pin, returns ACNT if
            /// past last AnalogPin. Sort of a redneck itererator :) 
            /// </summary>
            /// <returns></returns>
            public int nextAPin()
            {
                // stay within bounds
                if ((TestPoint)_analogIterator >= TestPoint.ACNT)
                {
                    _analogIterator = 0;
                }

                while ((_analogIterator < (int)TestPoint.ACNT)
                    && !((_AnalogUsed & (1 << _analogIterator)) > 0))
                {
                    _analogIterator++;
                }

                return _analogIterator++;
            }

        }

        public PinsUsed myUsedPins; // struct to keep track of pins used

        /// <summary>
        /// Encapsulates the info needed to report
        /// an analog test failure.
        /// </summary>
        public struct AnalogResult
        {
            TestPoint pin;
            double reading;
        }

        /// <summary>
        /// Encapsulates the info needed to report
        /// a digital test failure.
        /// </summary>
        public struct DigitalResult
        {
            public int drivePin; // this is the connectors pin 
            public int drivenPin;// this is the connectors pin 
            public bool reading;
        }

        /// <summary>
        /// A simple structure to hold any failures of last test run
        /// </summary>
        public struct LastTest
        {
            //public TestType type;
            public Queue aResult;
            public Queue dResult;

            public void reset()
            {
                if (aResult != null) { aResult.Clear(); }
                if (aResult != null & dResult.Count > 0)
                { 
                    dResult.Clear(); 
                }
            }
        }

        #endregion

        public LastTest myLastTest;

        #region Class_TestPort

        /// <summary>
        /// The one and only constructor.
        /// This class is intended to be overridden
        /// </summary>
        public TestPort()
        {
            // initialize the used pin tracker
            myUsedPins = new PinsUsed();
            myUsedPins.reset();

            // initialize the last test tracker
            myLastTest = new LastTest();
            myLastTest.dResult = new System.Collections.Queue();
            myLastTest.aResult = new System.Collections.Queue();
        }

        /// <summary>
        /// Digital test
        /// </summary>
        /// <param name="test"></param>
        /// <returns>true == passed</returns>
        public virtual bool digitalTest(TestConfig.Test test)
        {
            return true;
        }

        /// <summary>
        /// Accesor to results of last test
        /// </summary>
        /// <returns>LastTest structure which details any test failures</returns>
        public virtual LastTest lastTestResults()
        {
            return myLastTest;
        }

        /// <summary>
        /// Reset all previous results from last test
        /// </summary>
        public virtual void Reset()
        {
            myLastTest.reset();
        }

        #endregion
    }
}
